/*****************************************************************************
 *   Copyright 2005 Tim A Wang
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 ****************************************************************************/
package com.jb.web.bean.custom;

import com.jb.framework.PersistentContext;
import com.jb.web.application.EventManager;
import com.jb.web.bean.HtmlItem;
import com.jb.web.context.WebContext;

import java.text.SimpleDateFormat;

import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import com.jb.faces.context.FacesContext;


public class WebCalendar extends HtmlItem {
    static final long serialVersionUID = 123123L;

    static {
        publish(com.jb.web.bean.custom.WebCalendar.class, GENERAL, "cellRenderer");
        publish(com.jb.web.bean.custom.WebCalendar.class, GENERAL, "onSelect");

        publish(com.jb.web.bean.custom.WebCalendar.class, GENERAL, "todayStyle");
        publish(com.jb.web.bean.custom.WebCalendar.class, GENERAL, "holidayStyle");
        publish(com.jb.web.bean.custom.WebCalendar.class, GENERAL, "weekendStyle");
        publish(com.jb.web.bean.custom.WebCalendar.class, GENERAL, "cellStyle");
        publish(com.jb.web.bean.custom.WebCalendar.class, GENERAL, "weekStyle");
        publish(com.jb.web.bean.custom.WebCalendar.class, GENERAL, "showHeader");

        publish(com.jb.web.bean.custom.WebCalendar.class, GENERAL, "displayMode");
        publish(com.jb.web.bean.custom.WebCalendar.class, GENERAL, "selectedStyle");
    }

    public static final int MONTH = 2;
    public static final int YEAR = 3;
    public static final int WEEK = 1;
    public static final int DAY = 0;
    int[] daysInMonth = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    boolean showHeader;
    private Locale locale = Locale.getDefault();
    private Calendar calendar = Calendar.getInstance(locale);
    private Object userObject;
    private String cellRenderer;
    private String cellStyle;
    private String holidayStyle;
    private String onSelect;
    private String selectedStyle;
    private String todayStyle;
    
    private String weekStyle;
    private String weekendStyle;
    private int _displayMode = Calendar.MONTH;
    private int displayMode = 2;
    private int month;
    private int year;
    /**
     * Returns the begin date this calendar is for. The value depends on
     * the model of the calendar.
     * @return the beginning date.
     */
    public Calendar getBeginDate() {
        Calendar bd = (Calendar) calendar.clone();
        bd.set(Calendar.MINUTE, 0);
        bd.set(Calendar.HOUR_OF_DAY, 0);
        bd.set(Calendar.SECOND, 0);

        if (this.getDisplayMode() == DAY) {
            return bd;
        }

        if (this.getDisplayMode() == MONTH) {
            //  bd.add(Calendar.MONTH, 1);
            bd.set(Calendar.DAY_OF_MONTH, 1);
        }

        if (this.getDisplayMode() == WEEK) {
            int dow = bd.get(Calendar.DAY_OF_WEEK);
            bd.add(Calendar.DATE, -dow + 1);
        }

        return bd;
    }

    /**
     * Sets the class name of the cell renderer. It must implmement Renderer interface.
     * @param cellRenderer the class name of the renderer.
     */
    public void setCellRenderer(String cellRenderer) {
        this.cellRenderer = cellRenderer;
    }
    /**
     * Gets the class name of the cell renderer. 
     * @return class name of the renderer.
     */
    public String getCellRenderer() {
        return cellRenderer;
    }

    /**
     * Sets class name for a normal cell.
     *
     * @param cellStyle the class name for a cell.
     */
    public void setCellStyle(String cellStyle) {
        this.cellStyle = cellStyle;
    }

    /**
     * Gets class name for a normal cell.
     *
     * @return class name.
     */
    public String getCellStyle() {
        return cellStyle;
    }

    public int getDateAt(int col, int row) {
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        Calendar cal = Calendar.getInstance();
        cal.set(year, month, 1); //first day of the month

        int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); //determine which day is first day of month 
        int date = ((row * 7) + col) - dayOfWeek + 2;

        return ((date > 0) && (date <= getDays(year, month))) ? date : (-1);
    }

    public int getDate() {
        return calendar.get(Calendar.DATE);
    }

    /**
     * Tests if the date is selected. 
     *
     * @param date the date of the month.
     *
     * @return true is test is positive.
     */
    public boolean isDateSelected(int date) {
        return date == calendar.get(Calendar.DATE);
    }
    /**
     * Sets display mode. 
     * @param displayMode 
     * <ul>
     * <li>WebCalendar.DAY</li>
     * <li>WebCalendar.MONTH</li>
     * <li>WebCalendar.WEEK</li>
     * <li>WebCalendar.YEAR</li>
     * </ul>
     */
    public void setDisplayMode(int displayMode) {
        if (displayMode == DAY) {
            this._displayMode = Calendar.DATE;
        }

        if (displayMode == WEEK) {
            this._displayMode = Calendar.WEEK_OF_YEAR;
        }

        if (displayMode == MONTH) {
            this._displayMode = Calendar.MONTH;
        }

        if (displayMode == YEAR) {
            this._displayMode = Calendar.YEAR;
        }

        if (this.getFacesContext() != null) {
            this.processUpdates(getFacesContext());
        }
        this.displayMode = displayMode;
    }
    /**
     * Returns current display mode.
     * @return displayMode.
     */
    public int getDisplayMode() {
        return displayMode;
    }
    /**
     * Returns end date of the period. This value depends on the display mode.
     * @return the end date of the period.
     */
    public Calendar getEndDate() {
        Calendar bd = (Calendar) calendar.clone();
        bd.set(Calendar.MINUTE, 0);
        bd.set(Calendar.HOUR_OF_DAY, 0);
        bd.set(Calendar.SECOND, 0);

        if (this.getDisplayMode() == DAY) {
            bd.add(Calendar.DATE, 1);

            return bd;
        }

        if (this.getDisplayMode() == MONTH) {
            // int dom = bd.get(Calendar.DAY_OF_MONTH);
            bd.add(Calendar.MONTH, 1);
            bd.set(Calendar.DAY_OF_MONTH, 1);
        }

        if (this.getDisplayMode() == WEEK) {
            int dow = bd.get(Calendar.DAY_OF_WEEK);
            bd.add(Calendar.DATE, -dow);
            bd.add(Calendar.DATE, 8);
        }

        return bd;
    }

    /**
     * Sets class name for displaying holidays
     *
     * @param holidayStyle the class name for holidays.
     */
    public void setHolidayStyle(String holidayStyle) {
        this.holidayStyle = holidayStyle;
    }

    /**
     * Returns class name for displaying holidays.
     *
     * @return the class name for holidays.
     */
    public String getHolidayStyle() {
        return holidayStyle;
    }
    /**
     * Sets month of the year.
     * @param aMonth the month of the year.
     */
    public void setMonth(int aMonth) {
        if (month != aMonth) {
            month = aMonth;
            calendar.set(getYear(), month, 1);
        }
    }
    /**
     * Return the month of the year.
     * @return the month of the year.
     */
    public int getMonth() {
        return calendar.get(Calendar.MONTH);
    }
    /**
     * Moves the current date to next date, and return the date. 
     * @return next date.
     */
    public Date getNext() {
        calendar.add(this._displayMode, 1);

        int year = getYear();
        int month = getMonth();

        if ((month != getMonth()) || (year != getYear())) {
            updateEvents();
        }
        return calendar.getTime();
    }
    /**
     * Sets event handler for selecting a date.
     * @param onSelect event handler.
     */
    public void setOnSelect(String onSelect) {
        this.onSelect = onSelect;
    }

    /**
     * Returns event handler.
     *
     * @return onSelect event.
     */
    public String getOnSelect() {
        return onSelect;
    }
    /**
     * Move current date back one date, and return the date.
     * @return previous date.
     */
    public Date getPrevious() {
        int year = getYear();
        int month = getMonth();
        calendar.add(this._displayMode, -1);

        if ((month != getMonth()) || (year != getYear())) {
            updateEvents();
        }

        return calendar.getTime();
    }
    /**
     * Returns a formatted ranged text, from and to.
     * @return formatted range text.
     */
    public String getRangeText() {
        if (this.displayMode == MONTH) {
            return new SimpleDateFormat("MMMM, yyyy").format(calendar.getTime());
        }

        if (this.displayMode == DAY) {
            return new SimpleDateFormat("MMMM d, yyyy").format(calendar.getTime());
        }

        if (this.displayMode == WEEK) {
            return new SimpleDateFormat("MMM d").format(getBeginDate().getTime()) + " - " +
            new SimpleDateFormat("MMM d, yyyy").format(getEndDate().getTime());
        }

        return new SimpleDateFormat("MMMM d, yyyy").format(calendar.getTime());
    }
    /**
     * Sets selected date.
     * @param pDate the date to being selected.
     */
    public void setSelectedDate(Date pDate) {
        calendar.setTime(pDate);
        year = calendar.get(Calendar.YEAR);
        month = calendar.get(Calendar.MONTH);
    }
    /**
     * Sets selected date.
     * @param pDate the date of the month.
     * @param pMonth the month of the year.
     * @param pYear the year.
     */
    public void setSelectedDate(String pDate, String pMonth, String pYear) {
        int m = new Integer(pMonth).intValue();
        int y = new Integer(pYear).intValue();

        if (pDate != null) {
            calendar.set(y, m, new Integer(pDate).intValue(), 0, 0, 0);
        }

        year = calendar.get(Calendar.YEAR);
        month = calendar.get(Calendar.MONTH);
    }
    /**
     * Returns formatted day of the week.
     * @return day of the week.
     */
    public String getDayOfWeek() {
        return new SimpleDateFormat("E").format(calendar.getTime());
    }
    /**
     * Returns formatted month of year.
     * @return the month of year.
     */
    public String getMonthOfYear() {
        return new SimpleDateFormat("MMMMM").format(calendar.getTime());
    }

    /**
     * Returns formatted year.
     * @return the year.
     */
    public String getYearInString() {
        return new SimpleDateFormat("yyyy").format(calendar.getTime());
    }
    /**
     * Returns selected date 
     * @return Calendar selected date.
     */
    public Calendar getSelectedDate() {
        return calendar;
    }

    /**
     * Sets class name for selected cell.
     *
     * @param selectedStyle the class name for selected cell.
     */
    public void setSelectedStyle(String selectedStyle) {
        this.selectedStyle = selectedStyle;
    }

    /**
     * Gets class name of selected cell.
     *
     * @return teh class name of a selected cell.
     */
    public String getSelectedStyle() {
        return selectedStyle;
    }

    /**
     * Sets flag indicates where header is shown. This is view 
     * meta data used by a renderer.
     *
     * @param showHeader if ture the renderer should display 
     * the header.
     */
    public void setShowHeader(boolean showHeader) {
        this.showHeader = showHeader;
    }

    /**
     * Gets show header flag. True means a renderer should render a 
     * header element.
     *
     * @return true if header should be shown.
     */
    public boolean isShowHeader() {
        return showHeader;
    }

    /**
     * Tests a date to see if it is today. 
     * @param date the date of the month.
     * @return true if it is today.
     */
    public boolean isToday(int date) {
        Calendar today = Calendar.getInstance();

        return (today.get(Calendar.MONTH) == calendar.get(Calendar.MONTH)) &&
        (today.get(Calendar.YEAR) == calendar.get(Calendar.YEAR)) && (today.get(Calendar.DATE) == date);
    }
    /**
     * Sets class name for cell that displays today.
     * @param todayStyle the class name of style for today.
     */
    public void setTodayStyle(String todayStyle) {
        this.todayStyle = todayStyle;
    }
    /**
     * Gets the class name for cell showing today.
     * @return the class name for today.
     */
    public String getTodayStyle() {
        return todayStyle;
    }
    /**
     * Sets user object. This is a reference holder user what to associate with
     * this calendar.
     * @param userObject the user object to be linked
     */
    public void setUserObject(Object userObject) {
        this.userObject = userObject;
    }
    /**
     * Gets the user object linked to this calendar. Returns null if not set.
     * @return the user object.
     */
    public Object getUserObject() {
        return userObject;
    }
    /**
     * Gets the week of year of selected date.
     * @return the week of year.
     */
    public int getWeek() {
        return calendar.get(Calendar.WEEK_OF_YEAR);
    }
    /**
     * Tests if a date is weekend. Month and year is previously set.
     * @param date the date to be tested.
     * @return true if it is a week end.
     */
    public boolean isWeekEnd(int date) {
        Calendar today = Calendar.getInstance();
        today.set(year, month, date);

        int dow = today.get(Calendar.DAY_OF_WEEK);

        return (dow == Calendar.SUNDAY) || (dow == Calendar.SATURDAY);
    }

    /**
     * Sets class name for week cell.
     *
     * @param weekStyle the class name for displaying week.
     */
    public void setWeekStyle(String weekStyle) {
        this.weekStyle = weekStyle;
    }

    /**
     * Gets the class name for displaying week.
     *
     * @return the class name.
     */
    public String getWeekStyle() {
        return weekStyle;
    }

    /**
     * Sets style for weekend.
     *
     * @param weekendStyle the css style for weekend.
     */
    public void setWeekendStyle(String weekendStyle) {
        this.weekendStyle = weekendStyle;
    }

    /**
     * Returns style for renderring weekend.
     *
     * @return css style for weekend.
     */
    public String getWeekendStyle() {
        return weekendStyle;
    }
    /**
     * Sets current year for this calendar.
     * @param newYear the year.
     */
    public void setYear(int newYear) {
        if (year != newYear) {
            year = newYear;
            calendar.set(newYear, getMonth(), 1);
        }
    }
    /**
     * Returns current year set for this calendar.
     * @return year.
     */
    public int getYear() {
        return calendar.get(Calendar.YEAR);
    }

    /**
     * Initializes the object.
     */
    public void init() {
        super.init();
        calendar = Calendar.getInstance(Locale.getDefault());

        year = calendar.get(Calendar.YEAR);
        month = calendar.get(Calendar.MONTH);

        cellStyle = "calendarCell";
        weekStyle = "calendarWeek";
        holidayStyle = "calendarHoliday";
        weekendStyle = "calendarWeekend";
        selectedStyle = "calendarSelected";
    }
    //inherited from parent.
    public void postCreate(PersistentContext pCtx) {
        super.postCreate(pCtx);
        calendar = Calendar.getInstance(Locale.getDefault());

        year = calendar.get(Calendar.YEAR);
        month = calendar.get(Calendar.MONTH);
    }
    //inherited from parent.
    public void processUpdates(FacesContext pCtx) {
        WebContext ctx = (WebContext) pCtx;

        if (isMyEvent(ctx)) {
            String pMonth = ctx.getRequest().getParameter("month");
            String pYear = ctx.getRequest().getParameter("year");
            String pDate = ctx.getRequest().getParameter("date");
            boolean isSelectDate = pDate != null;

            if (pMonth == null) {
                pMonth = "" + month;
            }

            if (pDate == null) {
                pDate = "1";
            }

            if (pYear == null) {
                pYear = "" + year;
            }

            setSelectedDate( pDate, pMonth, pYear);

            if (isSelectDate && (onSelect != null) && (onSelect.length() > 0)) {
                EventManager.getInstance().handleEvent(ctx, this, onSelect);
            }
        }

        super.processUpdates(pCtx);
    }

    private void updateEvents() {
        //List list = getCalendarEvents((WebContext) ctx);
        //setModelBean(list);
    }


    private int getDays(int pYear, int pMonth) {
        if (1 == pMonth) {
            return (((0 == (pYear % 4)) && (0 != (pYear % 100))) || (0 == (pYear % 400))) ? 29 : 28;
        } else {
            return daysInMonth[pMonth];
        }
    }
}
